#include "sp.h"

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"

#define int32u unsigned int

static char User[80];
static char Spread_name[80];

static char Private_group[MAX_GROUP_NAME];
static mailbox Mbox;
static int Num_sent;
static int32u Previous_len;



static int To_exit = 0;

#define MAX_MESSLEN     102400
#define MAX_VSSETS      10
#define MAX_MEMBERS     100

#define TRUE                        1
#define FALSE                       0

#define STATE_INITIAL               0
#define STATE_USER                  1
#define STATE_USER_SERVER_CONNECTED 2
#define STATE_USER_JOIN             3

static void Print_menu();
static void User_command();
static void Read_message();
static void Usage(int argc, char *argv[]);
static void Print_help();
static void Bye();

struct UserNode {
    char userName[MAX_USERNAME_LEN];
    int numClientsActive;
    struct UserNode *next;
};
typedef struct UserNode UserNode;
typedef struct UserNode* UserNodePtr;
UserNodePtr usrNodePtrHead;

struct MessageNode {
    ChatMsgInfo chatMsgInfo;
    int lineNumber;
    struct MessageNode *next;
    struct MessageNode *prev;
};
typedef struct MessageNode MessageNode;
typedef struct MessageNode* MessageNodePtr;
MessageNodePtr msgNodePtrHead;

char *chatRoom = NULL;
int oldServerId = -1, isUserNameInitialized = FALSE, serverId;
int lineNum = 1;
char gChatroom[20];
char serverClientAllGroupName[80];
char serverPrivateGroupName[80];
char currentUser[MAX_USERNAME_LEN];
int currentState = 0;

int insertUsr(char* usr) {
    int usrAlreadyFound;
    UserNodePtr tempPtr = usrNodePtrHead;
    usrAlreadyFound = FALSE;
    while (tempPtr != NULL) {
        if (strncmp(tempPtr->userName, usr, MAX_USERNAME_LEN) == 0) {
            usrAlreadyFound = TRUE;
            tempPtr->numClientsActive++;
            break;
        }
        tempPtr = tempPtr->next;
    }

    if (usrAlreadyFound == FALSE) {
        tempPtr = (UserNodePtr) malloc(sizeof (UserNode));
        strncpy(tempPtr->userName, usr, MAX_USERNAME_LEN);
        tempPtr->next = usrNodePtrHead;
        tempPtr->numClientsActive = 1;
        usrNodePtrHead = tempPtr;
    }
    return usrAlreadyFound;
}

int removeUsr(char* usr) {
    UserNodePtr tempPtr;
    UserNodePtr prevUserNodePtr = NULL;
    tempPtr = usrNodePtrHead;
    while (tempPtr != NULL) {
        if (strncmp(tempPtr->userName, usr, MAX_USERNAME_LEN) == 0) {
            tempPtr->numClientsActive--;
            break;
        }
        prevUserNodePtr = tempPtr;
        tempPtr = tempPtr->next;
    }

    if (tempPtr != NULL && tempPtr->numClientsActive == 0) {
        if (prevUserNodePtr == NULL) {
            usrNodePtrHead = tempPtr->next;
        } else {
            prevUserNodePtr->next = tempPtr->next;
        }
        free(tempPtr);
        return TRUE;
    }
    return FALSE;
}

void freeAllUsr() {

    UserNodePtr tempPtr, toBeFreed;
    tempPtr = toBeFreed = usrNodePtrHead;
    while (tempPtr != NULL) {
        tempPtr = toBeFreed->next;
        free(toBeFreed);
        toBeFreed = tempPtr;
    }
    usrNodePtrHead = NULL;
}

void insertMsg(ChatMsgInfo *chatMsgInfo, int lineNum) {
    MessageNodePtr tempPtr;
    tempPtr = (MessageNodePtr) malloc(sizeof (MessageNode));
    tempPtr->lineNumber = lineNum;
    memcpy(&(tempPtr->chatMsgInfo), chatMsgInfo, sizeof (ChatMsgInfo));
    tempPtr->next = msgNodePtrHead;
    tempPtr->prev = NULL;
    if (msgNodePtrHead != NULL) {
        msgNodePtrHead->prev = tempPtr;
    }
    msgNodePtrHead = tempPtr;
}

void freeAllMsg() {
    MessageNodePtr tempPtr, toBeFreed;
    tempPtr = toBeFreed = msgNodePtrHead;
    while (tempPtr != NULL) {
        tempPtr = toBeFreed->next;
        free(toBeFreed);
        toBeFreed = tempPtr;
    }
    msgNodePtrHead = NULL;
}

void likeMsg(LTS *lts) {
    MessageNodePtr tempPtr = msgNodePtrHead;
    while (tempPtr != NULL) {
        if (memcmp(&(tempPtr->chatMsgInfo.lts), lts, sizeof (LTS)) == 0) {
            tempPtr->chatMsgInfo.numLikes++;
            break;
        }
        tempPtr = tempPtr->next;
    }
}

void unlikeMsg(LTS *lts) {
    MessageNodePtr tempPtr = msgNodePtrHead;
    while (tempPtr != NULL) {
        if (memcmp(&(tempPtr->chatMsgInfo.lts), lts, sizeof (LTS)) == 0) {
            tempPtr->chatMsgInfo.numLikes--;
            break;
        }
        tempPtr = tempPtr->next;
    }
}

void display(int totalLines, int isDisplayHeader, int isDisplayAttendees) {
    int tempCtr = 1;
    UserNodePtr tempUsrNodePtr = usrNodePtrHead;
    MessageNodePtr msgNode = msgNodePtrHead;
    int isFirst;

    printf("\n\n==============================================================");
    if (isDisplayHeader == TRUE) {
        printf("\nRoom: %s", chatRoom);
    }
    if (isDisplayAttendees == TRUE) {
        printf("\nAttendees: ");
        while (tempUsrNodePtr != NULL) {
            printf(" [%s] ", tempUsrNodePtr->userName);
            tempUsrNodePtr = tempUsrNodePtr->next;
        }
    }

    printf("\n");
    if (msgNodePtrHead != NULL && totalLines > 0) {
        isFirst = TRUE;
        while (msgNode->next != NULL && tempCtr < totalLines) {
            if (isFirst) {
                msgNode = msgNodePtrHead;
                isFirst = FALSE;
            } else {
                msgNode = msgNode->next;
            }
            tempCtr++;
        }

        while (msgNode != NULL) {
            printf("%d. %s: %s \t\t\t Likes: %d\n", msgNode->lineNumber, msgNode->chatMsgInfo.userName, msgNode->chatMsgInfo.msg, msgNode->chatMsgInfo.numLikes);
            msgNode = msgNode->prev;
        }
    }
    printf("==============================================================\n");
}

void resetDS() {
    freeAllUsr();
    freeAllMsg();
    lineNum = 1;
}

int main(int argc, char *argv[]) {
    int ret;
    int mver, miver, pver;
    sp_time test_timeout;

    test_timeout.sec = 5;
    test_timeout.usec = 0;

    Usage(argc, argv);
    if (!SP_version(&mver, &miver, &pver)) {
        printf("main: Illegal variables passed to SP_version()\n");
        Bye();
    }
    printf("Spread library version is %d.%d.%d\n", mver, miver, pver);

    ret = SP_connect_timeout(Spread_name, User, 0, 1, &Mbox, Private_group, test_timeout);
    if (ret != ACCEPT_SESSION) {
        SP_error(ret);
        Bye();
    }
    printf("User: connected to %s with private group %s\n", Spread_name, Private_group);

    strncpy(serverClientAllGroupName, SERVER_CLIENT_ALL, strlen(SERVER_CLIENT_ALL));
    strncat(serverClientAllGroupName, "X", 1);
    strncpy(serverPrivateGroupName, SERVER_PRIVATE_GROUP, strlen(SERVER_PRIVATE_GROUP));
    strncat(serverPrivateGroupName, "X", 1);

    E_init();

    E_attach_fd(0, READ_FD, User_command, 0, NULL, LOW_PRIORITY);

    E_attach_fd(Mbox, READ_FD, Read_message, 0, NULL, HIGH_PRIORITY);

    Print_menu();

    printf("\nUser> ");
    fflush(stdout);

    Num_sent = 0;

    E_handle_events();

    return ( 0);
}

static void User_command() {
    char command[130];

    char groups[10][MAX_GROUP_NAME];
    int num_groups;
    unsigned int mess_len;
    int ret;
    int i;

    char send_buf[MAX_MESSLEN];
    UserMsg *usrMsg;

    MessageNode *tempPtr;
    usrMsg = (UserMsg*) send_buf;
    char tmpSpace[130];


    for (i = 0; i < sizeof (command); i++) command[i] = 0;
    if (fgets(command, 130, stdin) == NULL)
        Bye();
    int j;
    switch (command[0]) {
        case 'u':

            sscanf(&command[2], "%s\0", tmpSpace);
            if (isUserNameInitialized == FALSE) {
                strncpy(currentUser, tmpSpace, MAX_USERNAME_LEN);
                isUserNameInitialized = TRUE;
                strncpy(usrMsg->userName, currentUser, MAX_USERNAME_LEN);
                currentState = STATE_USER;
            } else if ((strncmp(currentUser, tmpSpace, MAX_USERNAME_LEN) != 0)) {
                memset(currentUser, 0, MAX_USERNAME_LEN);
                strncpy(currentUser, tmpSpace, strlen(tmpSpace));
                for (i = 0, j = 0; i < MAX_USERNAME_LEN; i++, j++) {
                    if (j % 10 == 0) {
                        printf("\n");
                    }
                    printf("[%c] ", currentUser[i]);
                }
                printf("\n");
                if (chatRoom != NULL) {
                    ret = SP_leave(Mbox, gChatroom);
                    if (ret < 0) {
                        SP_error(ret);
                        Bye();
                    }

                    /*******Construct message to leave chatroom********/
                    usrMsg->type = USERMSG_LEAVE;

                    /*******Send message to leave chatroom********/
                    ret = SP_multicast(Mbox, FIFO_MESS, serverPrivateGroupName, CLIENT_TO_SERVER, sizeof (UserMsg), send_buf);
                    if (ret < 0) {
                        SP_error(ret);
                        Bye();
                    }
                    chatRoom = NULL;
                }
            }
            strncpy(usrMsg->userName, currentUser, MAX_USERNAME_LEN);
            printf("[%s] \n", usrMsg->userName);

            if (currentState == STATE_USER_JOIN) {
                currentState = STATE_USER_SERVER_CONNECTED;
            }
            break;

        case 'c':
            if (currentState != STATE_INITIAL) {
                /* TODO: Detect the presence of server */
                ret = sscanf(&command[2], "%d", &serverId);
                if (ret < 1) {
                    printf(" invalid server id \n");
                    break;
                }
                serverClientAllGroupName[strlen(serverClientAllGroupName) - 1] = command[2];
                serverPrivateGroupName[strlen(serverPrivateGroupName) - 1] = command[2];

                if (oldServerId == -1) {
                    ret = SP_join(Mbox, serverClientAllGroupName);
                    if (ret < 0) SP_error(ret);
                    oldServerId = serverId;
                    currentState = STATE_USER_SERVER_CONNECTED;
                } else if (oldServerId != serverId) {
                    ret = SP_leave(Mbox, serverClientAllGroupName);
                    if (ret < 0) SP_error(ret);
                    serverClientAllGroupName[strlen(serverClientAllGroupName) - 1] = command[2];
                    serverPrivateGroupName[strlen(serverPrivateGroupName) - 1] = command[2];
                    ret = SP_join(Mbox, serverClientAllGroupName);
                    if (ret < 0) SP_error(ret);
                    oldServerId = serverId;
                    currentState = STATE_USER_SERVER_CONNECTED;
                }
            } else {
                printf("Denied: Wrong choice.");
            }
            break;

        case 'j':
            if (currentState == STATE_USER_SERVER_CONNECTED
                    || currentState == STATE_USER_JOIN) {
                sscanf(&command[2], "%s", tmpSpace);
                if (chatRoom == NULL) {
                    constructChatRoomGroupName(tmpSpace, gChatroom, serverId);
                    printf("[%s]", tmpSpace);
                    printf("Joining Spread Group [%s] ", gChatroom);

                    ret = SP_join(Mbox, gChatroom);
                    if (ret < 0) SP_error(ret);

                    chatRoom = (char *) malloc(sizeof (MAX_CHATROOMID_LEN));
                } else if (strncmp(chatRoom, tmpSpace, MAX_CHATROOMID_LEN) != 0) {
                    ret = SP_leave(Mbox, gChatroom);
                    if (ret < 0) SP_error(ret);

                    /*******Construct message to leave chatroom********/
                    usrMsg->type = USERMSG_LEAVE;

                    /*******Send message to leave chatroom********/
                    ret = SP_multicast(Mbox, FIFO_MESS, serverPrivateGroupName, CLIENT_TO_SERVER, sizeof (UserMsg), send_buf);
                    if (ret < 0) {
                        SP_error(ret);
                        Bye();
                    }

                    /*******send message to join the new chatroom********/
                    constructChatRoomGroupName(chatRoom, gChatroom, serverId);
                    printf("Joining Spread Groupp [%s] ", gChatroom);
                    ret = SP_join(Mbox, gChatroom);
                    if (ret < 0) SP_error(ret);
                }

                ret = sscanf(&command[2], "%s", chatRoom);
                if (ret < 1) {
                    printf(" invalid chatroom \n");
                    break;
                }

                /*******Construct message to join chatroom********/
                strncpy(usrMsg->chatRoom, chatRoom, strlen(chatRoom));
                usrMsg->type = USERMSG_JOIN;

                /*******send message to join the new chatroom********/
                ret = SP_multicast(Mbox, FIFO_MESS, serverPrivateGroupName, CLIENT_TO_SERVER, sizeof (UserMsg), send_buf);
                if (ret < 0) {
                    SP_error(ret);
                    Bye();
                }
                currentState = STATE_USER_JOIN;
            } else {
                printf("Denied: Wrong choice.");
            }
            break;

        case 'a':
            if (currentState == STATE_USER_JOIN) {
                for (i = 0; i < MAX_MSG_LEN; i++) {
                    if (command[2 + i] != '\0' && command[2 + i] != '\n' && command[2 + i] != 10) {
                        tmpSpace[i] = command[2 + i];
                    } else {
                        break;
                    }
                }
                tmpSpace[i] = '\0';

                usrMsg->type = USERMSG_APPEND;
                strncpy(usrMsg->msg, tmpSpace, MAX_MSG_LEN);
                /*******send the chat message********/
                ret = SP_multicast(Mbox, FIFO_MESS, serverPrivateGroupName, CLIENT_TO_SERVER, sizeof (UserMsg), send_buf);
                if (ret < 0) {
                    SP_error(ret);
                    Bye();
                }
            } else {
                printf("Denied: Wrong choice.");
            }
            break;

        case 'l':
            if (currentState == STATE_USER_JOIN) {
                /* Like a message */
                sscanf(&command[2], "%s", tmpSpace);
                tempPtr = msgNodePtrHead;
                while (tempPtr != NULL) {
                    if (tempPtr->lineNumber == atoi(tmpSpace)) {
                        memcpy(usrMsg->msg, &(tempPtr->chatMsgInfo.lts), sizeof (LTS));
                        usrMsg->type = USERMSG_LIKE;

                        /*******send message to like********/
                        ret = SP_multicast(Mbox, FIFO_MESS, serverPrivateGroupName, CLIENT_TO_SERVER, sizeof (UserMsg), send_buf);
                        if (ret < 0) {
                            SP_error(ret);
                            Bye();
                        }
                        break;
                    } else {
                        tempPtr = tempPtr->next;
                    }
                }
            } else {
                printf("Denied: Wrong choice.");
            }
            break;

        case 'r':
            if (currentState == STATE_USER_JOIN) {
                /* Remove a like */
                tempPtr = msgNodePtrHead;
                while (tempPtr != NULL) {
                    if (tempPtr->lineNumber == atoi(&(command[2]))) {
                        memcpy(usrMsg->msg, &(tempPtr->chatMsgInfo.lts), sizeof (LTS));
                        usrMsg->type = USERMSG_UNLIKE;

                        /*******send message to remove the like********/
                        ret = SP_multicast(Mbox, FIFO_MESS, serverPrivateGroupName, CLIENT_TO_SERVER, sizeof (UserMsg), send_buf);
                        if (ret < 0) {
                            SP_error(ret);
                            Bye();
                        }
                        break;
                    } else {
                        tempPtr = tempPtr->next;
                    }
                }
                break;
            } else {
                printf("Denied: Wrong choice.");
            }

        case 'h':
            if (currentState == STATE_USER_JOIN) {
                /*******Construct message to join chatroom********/
                usrMsg->type = USERMSG_HISTORY;

                /*******send message to view history********/
                ret = SP_multicast(Mbox, FIFO_MESS, serverPrivateGroupName, CLIENT_TO_SERVER, sizeof (UserMsg), send_buf);
                if (ret < 0) {
                    SP_error(ret);
                    Bye();
                }
            } else {
                printf("Denied: Wrong choice.");
            }
            break;

        case 'v':
            if (currentState != STATE_INITIAL || currentState != STATE_USER) {
                usrMsg->type = USERMSG_VIEW;
               ret = SP_multicast(Mbox, FIFO_MESS, serverPrivateGroupName, CLIENT_TO_SERVER, sizeof (UserMsg), send_buf);
                if (ret < 0) {
                    SP_error(ret);
                    Bye();
                }
            } else {
                printf("Denied: Wrong choice.");
            }
            break;

        case 'q':

            /******************DELETE ME***********************/
            ret = SP_leave(Mbox, gChatroom);
            if (ret < 0) {
                SP_error(ret);
                Bye();
            }

            /*******Construct message to leave chatroom********/
            usrMsg->type = USERMSG_LEAVE;

            /*******Send message to leave chatroom********/
            ret = SP_multicast(Mbox, FIFO_MESS, serverPrivateGroupName, CLIENT_TO_SERVER, sizeof (UserMsg), send_buf);
            if (ret < 0) {
                SP_error(ret);
                Bye();
            }
            /******************DELETE ME*********************/

            Bye();
            break;

        default:
            printf("\nUnknown command\n");
            Print_menu();
            break;
    }
    printf("\nUser> ");
    fflush(stdout);

}

static void Print_menu() {
    printf("\n");
    printf("==========\n");
    printf("Chat Menu:\n");
    printf("----------\n");
    printf("\n");
    printf("\tu <username> -- login \n");
    printf("\tc <serverid> -- connect to a specific server[1-5]\n");
    printf("\tj <group> -- join a chatroom\n");
    printf("\n");
    printf("\ta <message> -- send a chat\n");
    printf("\tl <line number> -- like a message\n");
    printf("\tr <line number> -- remove a like\n");
    printf("\n");
    printf("\th -- get the chat history \n");
    printf("\tv -- current view of servers \n");
    printf("\tq -- quit\n");
    fflush(stdout);
}

/* FIXME: The user.c code does not use memcpy()s to avoid bus errors when
 *        dereferencing a pointer into a potentially misaligned buffer */

static void Read_message() {

    static char mess[MAX_MESSLEN];
    char sender[MAX_GROUP_NAME];
    char target_groups[MAX_MEMBERS][MAX_GROUP_NAME];
    membership_info memb_info;
    vs_set_info vssets[MAX_VSSETS];
    unsigned int my_vsset_index;
    int num_vs_sets;
    char members[MAX_MEMBERS][MAX_GROUP_NAME];
    int num_groups;
    int service_type;
    int16 mess_type;
    int endian_mismatch;
    int i, j;
    int ret;
    char tmpSpace[130];

    service_type = 0;

    char recv_buf[MAX_MESSLEN];
    ClientMsgHdr *clientMsgHdrPtr;
    char *tempPtr;
    ServerMsg *serverMsgPtr;
    ChatMsgInfo *chatMsgInfo;
    int msgLength, isDetected = FALSE;
    char *data;
    int loopCtr;


    ret = SP_receive(Mbox, &service_type, sender, 100, &num_groups, target_groups,
            &mess_type, &endian_mismatch, sizeof (recv_buf), recv_buf);
    printf("\n============================\n");
    if (ret < 0) {
        if ((ret == GROUPS_TOO_SHORT) || (ret == BUFFER_TOO_SHORT)) {
            service_type = DROP_RECV;
            printf("\n========Buffers or Groups too Short=======\n");
            ret = SP_receive(Mbox, &service_type, sender, MAX_MEMBERS, &num_groups, target_groups,
                    &mess_type, &endian_mismatch, sizeof (mess), mess);
        }
    }
    if (ret < 0) {
        if (!To_exit) {
            SP_error(ret);
            printf("\n============================\n");
            printf("\nBye.\n");
        }
        exit(0);
    }
    if (Is_regular_mess(service_type)) {
        mess[ret] = 0;

        switch (mess_type) {
            case SERVER_TO_CLIENT:
                /**********************  1. From a server  **********************/
                printf("\nRecieved message to Client Alone\n");
                resetDS();
                clientMsgHdrPtr = (ClientMsgHdr*) recv_buf;

                msgLength = sizeof (ClientMsgHdr) + (clientMsgHdrPtr->numUsers) * MAX_USERNAME_LEN
                        + (clientMsgHdrPtr->numOfChatMsgs) * sizeof (ChatMsgInfo);
/*
                for (i = 0, j = 0; i < msgLength; i++, j++) {
                    if (j % 10 == 0) {
                        printf("\n");
                    }
                    printf("[%c] ", recv_buf[i]);
                }
                printf("\n");
*/

                printf("Message Count: [%d] UserCount: [%d] \n", clientMsgHdrPtr->numOfChatMsgs, clientMsgHdrPtr->numUsers);
                tempPtr = recv_buf + sizeof (ClientMsgHdr);

                printf("UserList: ");
                for (i = 0; i < clientMsgHdrPtr->numUsers; i++) {
                    insertUsr(tempPtr);
                    printf("[%s] ", tempPtr);
                    tempPtr = tempPtr + MAX_USERNAME_LEN;
                }


                printf("\nChat Msg List: ");
                tempPtr = tempPtr + (clientMsgHdrPtr->numOfChatMsgs * sizeof (ChatMsgInfo));
                for (i = 0; i < clientMsgHdrPtr->numOfChatMsgs; i++) {
                    tempPtr = tempPtr - sizeof (ChatMsgInfo);
                    insertMsg((ChatMsgInfo*) tempPtr, lineNum++);
                    printChatMsg((ChatMsgInfo*) tempPtr);
                }

                display(DEFAULT_NUM_MSGS, TRUE, TRUE);
                break;

            case SERVER_TO_CLIENT_GROUP:

                serverMsgPtr = (ServerMsg*) recv_buf;
                printf("\nRecieved message to Client Group\n");
                printServerMsg(serverMsgPtr);
                switch (serverMsgPtr->userMsg.type) {
                    case USERMSG_JOIN:
                        if(insertUsr(serverMsgPtr->userMsg.userName) == FALSE) {
                            printf("User [%s] joined the room \n", serverMsgPtr->userMsg.userName);
                            display(0, FALSE, TRUE);
                        }
                        break;
                    case USERMSG_LEAVE:
                        if (removeUsr(serverMsgPtr->userMsg.userName) == TRUE) {
                            printf("User [%s] left the room \n", serverMsgPtr->userMsg.userName);
                            display(0, FALSE, TRUE);
                        }
                        break;
                    case USERMSG_APPEND:
                        chatMsgInfo = (ChatMsgInfo*) malloc(sizeof (ChatMsgInfo));
                        memcpy(&(chatMsgInfo->lts), &(serverMsgPtr->lts), sizeof (LTS));
                        memcpy(chatMsgInfo->msg, serverMsgPtr->userMsg.msg, MAX_MSG_LEN);
                        strncpy(chatMsgInfo->userName, serverMsgPtr->userMsg.userName, MAX_USERNAME_LEN);
                        chatMsgInfo->numLikes = 0;
                        insertMsg(chatMsgInfo, lineNum++);
                        display(DEFAULT_NUM_MSGS, TRUE, TRUE);
                        break;
                    case USERMSG_LIKE:
                        likeMsg((LTS *) (serverMsgPtr->userMsg.msg));
                        display(DEFAULT_NUM_MSGS, TRUE, TRUE);
                        break;
                    case USERMSG_UNLIKE:
                        unlikeMsg((LTS *) (serverMsgPtr->userMsg.msg));
                        display(DEFAULT_NUM_MSGS, TRUE, TRUE);
                        break;
                }

            case SERVER_TO_CLIENT_DEBUG:
                data = recv_buf;
                printf("View of the Server [%d] is as follows\n", serverId);
                for(loopCtr = 0; loopCtr < NUM_SERVERS; loopCtr++) {
                    printf("Highest LTS of Server[%d] known is - ", ((ServerInfo*)data)->serverId);
                    printLTS( &(((ServerInfo*)data)->highestLts) );
                    printf("\n");
                    data = data + sizeof(ServerInfo);
                }
                break;
        }



    } else if (Is_membership_mess(service_type)) {
        ret = SP_get_memb_info(mess, service_type, &memb_info);
        if (ret < 0) {
            printf("BUG: membership message does not have valid body\n");
            SP_error(ret);
            exit(1);
        }
        if (Is_reg_memb_mess(service_type)) {
            /* TODO: Handle server disconnect. Ignore all other membership changes */
            printf("Received REGULAR membership for group %s with %d members, where I am member %d:\n",
                    sender, num_groups, mess_type);

            for (i = 0; i < num_groups; i++) {
                printf("\t%s\n", &target_groups[i][0]);
            }

            /****************Detect the presence of server****************/
            if (strncmp(sender, SERVER_CLIENT_ALL, strlen(SERVER_CLIENT_ALL)) == 0) {
                isDetected = FALSE;
                for (i = 0; i < num_groups; i++) {
                    if (strncmp(target_groups[i], "#server", 7) == 0) {
                        isDetected = TRUE;
                    }
                }
                if (isDetected == FALSE) {
                    printf("Server connection lost OR Unable to connect.\n");
                    oldServerId = -1;
                    currentState = STATE_USER;
                    ret = SP_leave(Mbox, serverClientAllGroupName);
                    if (ret < 0) SP_error(ret);
                    ret = SP_leave(Mbox, gChatroom);
                    if (ret < 0) SP_error(ret);
                }
            }
            /****************Detect the presence of server****************/

            printf("grp id is %d %d %d\n", memb_info.gid.id[0], memb_info.gid.id[1], memb_info.gid.id[2]);

            if (Is_caused_join_mess(service_type)) {
                printf("Due to the JOIN of %s\n", memb_info.changed_member);
            } else if (Is_caused_leave_mess(service_type)) {
                printf("Due to the LEAVE of %s\n", memb_info.changed_member);
            } else if (Is_caused_disconnect_mess(service_type)) {
                printf("Due to the DISCONNECT of %s\n", memb_info.changed_member);
            } else if (Is_caused_network_mess(service_type)) {
                printf("Due to NETWORK change with %u VS sets\n", memb_info.num_vs_sets);
                num_vs_sets = SP_get_vs_sets_info(mess, &vssets[0], MAX_VSSETS, &my_vsset_index);
                if (num_vs_sets < 0) {
                    printf("BUG: membership message has more then %d vs sets. Recompile with larger MAX_VSSETS\n", MAX_VSSETS);
                    SP_error(num_vs_sets);
                    exit(1);
                }
                for (i = 0; i < num_vs_sets; i++) {
                    printf("%s VS set %d has %u members:\n",
                            (i == my_vsset_index) ?
                            ("LOCAL") : ("OTHER"), i, vssets[i].num_members);
                    ret = SP_get_vs_set_members(mess, &vssets[i], members, MAX_MEMBERS);
                    if (ret < 0) {
                        printf("VS Set has more then %d members. Recompile with larger MAX_MEMBERS\n", MAX_MEMBERS);
                        SP_error(ret);
                        exit(1);
                    }
                    for (j = 0; j < vssets[i].num_members; j++)
                        printf("\t%s\n", members[j]);
                }
            }
        } else if (Is_transition_mess(service_type)) {
/*
            printf("received TRANSITIONAL membership for group %s\n", sender);
*/
        } else if (Is_caused_leave_mess(service_type)) {
            printf("received membership message that left group %s\n", sender);
        } else printf("received incorrecty membership message of type 0x%x\n", service_type);
    } else if (Is_reject_mess(service_type)) {
        printf("REJECTED message from %s, of servicetype 0x%x messtype %d, (endian %d) to %d groups \n(%d bytes): %s\n",
                sender, service_type, mess_type, endian_mismatch, num_groups, ret, mess);
    } else printf("received message of unknown message type 0x%x with ret %d\n", service_type, ret);


    printf("\n");
    printf("User> ");
    fflush(stdout);
}

static void Usage(int argc, char *argv[]) {
    sprintf(User, "user");
    sprintf(Spread_name, "10900@localhost");
    while (--argc > 0) {
        argv++;

        if (!strncmp(*argv, "-u", 2)) {
            if (argc < 2) Print_help();
            strcpy(User, argv[1]);
            argc--;
            argv++;
        } else if (!strncmp(*argv, "-r", 2)) {
            strcpy(User, "");
        } else if (!strncmp(*argv, "-s", 2)) {
            if (argc < 2) Print_help();
            strcpy(Spread_name, argv[1]);
            argc--;
            argv++;
        } else {
            Print_help();
        }
    }
}

static void Print_help() {
    printf("Usage: spuser\n%s\n%s\n%s\n",
            "\t[-u <user name>]  : unique (in this machine) user name",
            "\t[-s <address>]    : either port or port@machine",
            "\t[-r ]    : use random user name");
    exit(0);
}

static void Bye() {
    To_exit = 1;

    printf("\nBye.\n");

    SP_disconnect(Mbox);

    exit(0);
}